﻿using IOP.Extension.Convert;
using IOP.Protocols.MQTT.Extension;
using System;
using System.Text;
using System.Text.RegularExpressions;

namespace IOP.Protocols.MQTT.Package
{
    /// <summary>
    /// 执行连接包
    /// </summary>
    public struct ConnectPackage : IMQTTPackage
    {
        /// <summary>
        /// 固定大小
        /// </summary>
        const int FixLength = 12;

        /// <summary>
        /// 报文类型
        /// </summary>
        public ControlPacketType PacketType { get => ControlPacketType.CONNECT; }

        private uint _RemainingLength;
        /// <summary>
        /// 剩余长度
        /// </summary>
        public uint RemainingLength 
        {
            get => _RemainingLength + FixLength;
        }

        /// <summary>
        /// 协议名
        /// </summary>
        public string ProtocolName { get => "MQTT"; }
        /// <summary>
        /// 协议级别
        /// </summary>
        public byte ProtocolLevel;
        /// <summary>
        /// 清除会话
        /// </summary>
        public bool CleanSession;
        /// <summary>
        /// 遗嘱QoS
        /// </summary>
        public QoSType WillQoS;
        /// <summary>
        /// 遗嘱保留
        /// </summary>
        public bool WillRetain;
        /// <summary>
        /// 保持连接
        /// </summary>
        public ushort KeepAlive;

        private bool _WillFlag;
        /// <summary>
        /// 遗嘱标志
        /// </summary>
        public bool WillFlag
        {
            get => _WillFlag;
            set
            {
                if(_WillFlag != value)
                {
                    if (value) _RemainingLength += (uint)(4 + WillTopic.Length + WillMessage.Length);
                    else _RemainingLength -= (uint)(4 + WillTopic.Length + WillMessage.Length);
                    _WillFlag = value;
                }
            }
        }

        private bool _UserNameFlag;
        /// <summary>
        /// 用户名标志
        /// </summary>
        public bool UserNameFlag
        {
            get => _UserNameFlag;
            set
            {
                if(_UserNameFlag != value)
                {
                    if (value) _RemainingLength += (uint)(2 + UserName.Length);
                    else _RemainingLength -= (uint)(2 + UserName.Length);
                    _UserNameFlag = value;
                }
            }
        }

        private bool _PasswordFlag;
        /// <summary>
        /// 密码标志
        /// </summary>
        public bool PasswordFlag
        {
            get => _PasswordFlag;
            set
            {
                if(_PasswordFlag != value)
                {
                    if(value) _RemainingLength += (uint)(2 + Password.Length);
                    else _RemainingLength -= (uint)(2 + Password.Length);
                    _PasswordFlag = value;
                }
            }
        }

        private string _ClientIdentifier;
        /// <summary>
        /// 客户端标识
        /// </summary>
        public string ClientIdentifier
        {
            get 
            {
                if (_ClientIdentifier == null) _ClientIdentifier = string.Empty;
                return _ClientIdentifier; 
            }
            set
            {
                if(_ClientIdentifier != value)
                {
                    if (_ClientIdentifier != null) _RemainingLength -= (uint)(_ClientIdentifier.Length);
                    if (value != null)
                    {
                        if (!Regex.IsMatch(value, @"^[A-Za-z0-9]+$")) throw new MQTTPackageException("ClientIdentifier have Illegal characters");
                        if (Encoding.UTF8.GetByteCount(value) > ushort.MaxValue) throw new MQTTPackageException("ClientIdentifier string lenght is out of 65535 bytes");
                        _RemainingLength += (uint)value.Length;
                        _ClientIdentifier = value;
                    }
                    else _ClientIdentifier = string.Empty;
                }
            }
        }

        private string _WillTopic;
        /// <summary>
        /// 遗嘱主题
        /// </summary>
        public string WillTopic
        {
            get 
            {
                if (_WillTopic == null) _WillTopic = string.Empty;
                return _WillTopic; 
            }
            set
            {
                if(_WillTopic != value)
                {
                    if (_WillTopic != null && _WillFlag) _RemainingLength -= (uint)_WillTopic.Length;
                    if (value != null)
                    {
                        if (Encoding.UTF8.GetByteCount(value) > ushort.MaxValue) throw new MQTTPackageException("WillTopic string lenght is out of 65535 bytes");
                        if (_WillFlag) _RemainingLength += (uint)value.Length;
                        _WillTopic = value;
                    }
                    else _WillTopic = string.Empty;
                }
            }
        }

        private byte[] _WillMessage;
        /// <summary>
        /// 遗嘱消息
        /// </summary>
        public byte[] WillMessage
        {
            get 
            {
                if (_WillMessage == null) _WillMessage = new byte[0];
                return _WillMessage; 
            }
            set
            {
                if(_WillMessage != value)
                {
                    if (_WillMessage != null && _WillFlag) _RemainingLength -= (uint)_WillMessage.Length;
                    if (value != null)
                    {
                        if (value.Length > ushort.MaxValue) throw new MQTTPackageException("WillTopic string lenght is out of 65535 bytes");
                        if (_WillFlag) _RemainingLength += (uint)value.Length;
                        _WillMessage = value;
                    }
                    else _WillMessage = new byte[0];
                }
            }
        }

        private string _UserName;
        /// <summary>
        /// 用户名
        /// </summary>
        public string UserName
        {
            get 
            {
                if (_UserName == null) _UserName = string.Empty;
                return _UserName; 
            }
            set
            {
                if(_UserName != value)
                {
                    if (_UserName != null && _UserNameFlag) _RemainingLength -= (uint)_UserName.Length; 
                    if (value != null)
                    {
                        if (Encoding.UTF8.GetByteCount(value) > ushort.MaxValue) throw new MQTTPackageException("UserName string lenght is out of 65535 bytes");
                        if (_UserNameFlag) _RemainingLength += (uint)value.Length;
                        _UserName = value;
                    }
                    else _UserName = string.Empty;
                }
            }
        }

        private byte[] _Password;
        /// <summary>
        /// 密码
        /// </summary>
        public byte[] Password
        {
            get 
            {
                if (_Password == null) _Password = new byte[0];
                return _Password; 
            }
            set
            {
                if(_Password != value)
                {
                    if (_Password != null && _PasswordFlag) _RemainingLength -= (uint)_Password.Length;
                    if (value != null)
                    {
                        if (value.Length > ushort.MaxValue) throw new MQTTPackageException("Password lenght is out of 66535 bytes");
                        if (_PasswordFlag) _RemainingLength += (uint)value.Length;
                        _Password = value;
                    }
                    else _Password = new byte[0];
                }
            }
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="connectOption"></param>
        public ConnectPackage(MQTTConnectOption connectOption)
        {
            _RemainingLength = 0;
            _UserNameFlag = false;
            _PasswordFlag = false;
            _WillFlag = false;
            _ClientIdentifier = string.Empty;
            _WillTopic = string.Empty; ;
            _WillMessage = new byte[0];
            _UserName = string.Empty;
            _Password = new byte[0];

            ProtocolLevel = connectOption.ProtocolLevel;
            CleanSession = connectOption.CleanSession;
            KeepAlive = connectOption.KeepAlive;
            WillQoS = connectOption.WillQoS;
            WillRetain = connectOption.WillRetain;
            ClientIdentifier = connectOption.ClientIdentifier;

            PasswordFlag = connectOption.PasswordFlag;
            Password = connectOption.Password;

            UserNameFlag = connectOption.UserNameFlag;
            UserName = connectOption.UserName;

            WillFlag = connectOption.WillFlag;
            WillTopic = connectOption.WillTopic;
            WillMessage = connectOption.WillMessage;
        }

        /// <summary>
        /// 获取连接标识符
        /// </summary>
        /// <returns></returns>
        private byte GetConnectFlags()
        {
            var session = (CleanSession ? 1 : 0) << 1;
            var flag = (WillFlag ? 1 : 0) << 2;
            var qos = (int)WillQoS << 3;
            var retain = (WillRetain ? 1 : 0) << 5;
            var user = (UserNameFlag ? 1 : 0) << 7;
            var pass = (PasswordFlag ? 1 : 0) << 6;
            return (byte)(session | flag | qos | retain | user | pass);
        }

        /// <summary>
        /// 转换为字节码
        /// </summary>
        /// <returns></returns>
        public Span<byte> ToBytes()
        {
            int remaining = RemainingLength.GetRemainBytesLength();
            var length = RemainingLength + remaining + 1;

            Span<byte> result = new byte[length];
            int index = 0;
            result[index++] = (byte)((byte)PacketType << 4);
            RemainingLength.WriteRemainLengthToBytes(ref result, ref index);

            ProtocolName.SrtingToMqttUTF8Bytes(ref result, ref index);
            result[index++] = ProtocolLevel;
            result[index++] = GetConnectFlags();
            KeepAlive.WriteBytes(ref result, ref index);

            ClientIdentifier.SrtingToMqttUTF8Bytes(ref result, ref index);

            if (WillFlag)
            {
                WillTopic.SrtingToMqttUTF8Bytes(ref result, ref index);
                ushort willLength = (ushort)WillMessage.Length;
                willLength.WriteBytes(ref result, ref index);
                WillMessage.AsSpan().TryCopyTo(result[index..(index + WillMessage.Length)]);
                index += WillMessage.Length;
            }

            if (UserNameFlag)
            {
                UserName.SrtingToMqttUTF8Bytes(ref result, ref index);
            }

            if (PasswordFlag)
            {
                var pwdLength = (ushort)Password.Length;
                pwdLength.WriteBytes(ref result, ref index);
                Password.AsSpan().TryCopyTo(result[index..(index + Password.Length)]);
            }
            return result;
        }
    }
}
